#include "f_scores.cpp"

inline void printTestResults(Result& r)
{
    std::vector<double> fscores = calculateFScores(r);
    double totalF = calculateTotalFScore(r);

    std::cout << "Total correct: " << r.correct_ones() << "/1000" << std::endl;
    std::cout << "Type  WHITE " <<r.correct_types[0] << "/" << r.total_types[0] << "   F-score: "  << fscores[7] <<  std::endl;
    std::cout << "Type  RED " << r.correct_types[1] << "/" << r.total_types[1] << "   F-score: "  << fscores[8]<< std::endl;

    std::cout << "F-scores for qualities:" << std::endl;
    for(int i = 0; i < 7; i++){
        std::cout << fscores[i] << " ";
    }

    std::cout<<std::endl<< "TOTAL F-SCORE: "<< totalF << std::endl;

}

inline void printCombinedResults(Result& all, Result& red, Result& white)
{
    std::vector<double> fscores = calculateFScoresFromTwoStep(all,white,red);
    double totalF = calculateTotalFScoreFromTwoStep(all,white,red);
    std::cout << "Total correct: " << red.correct_ones()+white.correct_ones() << "/1000" << std::endl;
    std::cout << "Type  WHITE " <<white.correct_types[0] << "/" << all.total_types[0] << "   F-score: "  <<fscores[7]  <<  std::endl;
    std::cout << "Type  RED " << red.correct_types[1] << "/" << all.total_types[1] << "   F-score: "  << fscores[8]<< std::endl;



    std::cout << "F-scores for qualities:" << std::endl;
    for(int i = 0; i < 7; i++){
        std::cout << fscores[i] << "  ";
    }
    std::cout<<std::endl<< "TOTAL F-SCORE: "<< totalF << std::endl;
}


inline void testDistanceMethods()
{
    std::string file = "training_set.csv";
    Cellar training(file);
    std::vector<Wine> test = training.split_cellar(1000);
    std::vector<Wine*> testing;
    for(auto i = test.begin(); i != test.end();i++)
        testing.push_back(&(*i));
    Result r;


    //MANHATTAN & SQUARED_EUCLIDEAN DISTANCE TESTS
    for (int distanceType = 1; distanceType <= 2; distanceType++)
    {
        std::cout << "-------------- DISTANCE TYPE: " << distanceType << " -------------" << std::endl;
        //Change distance calculation method
        // 1 == MANHATTAN
        // 2 == SQUARED_EUCLIDEAN

        training.setDistanceType(distanceType);

        //Test with k-values 1-20
        for (int i = 1; i <= 20 ; i++)
        {
            training.setNumOfWinesForAveragePredict(i); //change value of k
            r = evaluate(training, testing, r);
            std::cout <<"K value: " << i << "   Correct: " << r.correct_ones() << std::endl;
        }
    }

    //MINKOWSKI DISTANCE TESTS
    int distanceType = 3;
    training.setDistanceType(distanceType);
    for(int i = 1; i<= 10; i++)
    {
        //double currentQ = 0.2*i;
        double currentQ = 0.2 +i*0.05;
        std::cout << "-------------- MINKOWSKI WITH Q: " << currentQ << " -------------" << std::endl;
        training.setQforMinkowski(currentQ); // test with different values of q: 0.2,0.4,0.6 ...

        //Test with k-values 1-20
        for (int i = 1; i <= 20 ; i++)
        {
            training.setNumOfWinesForAveragePredict(i); //change value of k
            r = evaluate(training, testing, r);
            std::cout <<"K value: " << i << "   Correct: " << r.correct_ones() << std::endl;
        }
    }
}

inline void testTypePrediction()
{
    std::string file = "training_set.csv";
    Cellar training(file);
    std::vector<Wine> test = training.split_cellar(1000);
    std::vector<Wine*> testing;
    for(auto i = test.begin(); i != test.end();i++)
        testing.push_back(&(*i));
    Result r;

    double qForMinkowski = 0.25;

    for (int distanceType = 1; distanceType <= 3; distanceType++)
    {
        std::cout << "-------------- DISTANCE TYPE: " << distanceType << " -------------" << std::endl;
        //Change distance calculation method
        // 1 == MANHATTAN
        // 2 == SQUARED_EUCLIDEAN
        // 3 == MINKOWSKI
        training.setDistanceType(distanceType);
        if(distanceType == 3) //Minkowski
            training.setQforMinkowski(qForMinkowski);

        r = evaluate(training, testing, r);
        std::cout <<"RED: " << r.correct_types[1] << "/" << r.total_types[1] << "  WHITE: " <<r.correct_types[0] << "/" << r.total_types[0]<< std::endl;
    }

    /**
-------------- DISTANCE TYPE: 1 -------------
RED: 175/177  WHITE: 819/823
-------------- DISTANCE TYPE: 2 -------------
RED: 175/177  WHITE: 818/823
-------------- DISTANCE TYPE: 3 -------------
RED: 175/177  WHITE: 821/823

==> Minkowski is the best

    **/
}



inline void finalTests()
{
    std::string file = "training_set.csv";
    Cellar training(file);
    file = "test_set.csv";
    Cellar temp_cellar(file, training.getLow(), training.getDif());
    std::vector<Wine> test = temp_cellar.split_cellar(1000);

    std::vector<Wine*> testing;
    for(auto i = test.begin(); i != test.end();i++)
        testing.push_back(&(*i));

    //Minkowski
    int distanceType = 3;
    training.setDistanceType(distanceType);

    //value for q
    double qValue = 0.25;
    training.setQforMinkowski(qValue);

    //k-value
    training.setNumOfWinesForAveragePredict(3);

    Result r;
    r = evaluate(training, testing, r);
    printTestResults(r);

    /**
    Total correct: 669/1000
    Type  WHITE 797/804   F-score: 0.995006
    Type  RED 195/196   F-score: 0.979899
    F-scores for qualities:
    0 0.486486 0.627778 0.67933 0.715152 0.325581 0
    TOTAL F-SCORE: 0.8305
    **/
}

inline void finalTwoStepTest()
{
    std::string file = "training_set.csv";
    Cellar training(file);
    file = "test_set.csv";
    Cellar temp_cellar(file, training.getLow(), training.getDif());
    std::vector<Wine> test = temp_cellar.split_cellar(1000);

    std::vector<Wine*> testing;
    for(auto i = test.begin(); i != test.end();i++)
        testing.push_back(&(*i));

    //Minkowski
    int distanceType = 3;
    training.setDistanceType(distanceType);

    //value for q
    double qValue = 0.25;
    training.setQforMinkowski(qValue);

    //k-value
    training.setNumOfWinesForAveragePredict(3);

    Result all;
    Result red;
    Result white;

    //FIRST STEP
    all = evaluate(training, testing, all);

    //Split training data to red and white
    std::pair<Cellar, Cellar> redAndWhiteTraining = training.splitToRedAndWhite();

    // Split to red and white wines based on predictions
    std::pair<std::vector<Wine*>, std::vector<Wine*> > redAndWhiteTesting = Cellar::splitWineListToRedAndWhite(testing);

    //SECOND STEP
    //Red wines against red wines
    red = evaluate(redAndWhiteTraining.first, redAndWhiteTesting.first, red);
    //White wines against white wines
    white = evaluate(redAndWhiteTraining.second, redAndWhiteTesting.second, white);

    //Combine results
    printCombinedResults(all, red, white);

    /**
    Total correct: 670/1000
    Type  WHITE 797/804   F-score: 0.995006
    Type  RED 195/196   F-score: 0.979899
    F-scores for qualities:
    0  0.486486  0.627778  0.678571  0.720365  0.318182  0
    TOTAL F-SCORE: 0.831

    SLIGHTLY BETTER THAN 1 STEP

    **/
}


// Tests for first predicting the type of wine and then
// predicting the quality separately for white and red wines
inline void testTwoStepPrediction()
{
    std::string file = "training_set.csv";
    Cellar training(file);
    std::vector<Wine> test = training.split_cellar(1000);
    std::vector<Wine*> testing;
    for(auto i = test.begin(); i != test.end();i++)
        testing.push_back(&(*i));
    Result all;
    Result red;
    Result white;

    //Let's use Minkowski because it gives the best results
    int distanceType = Cellar::MINKOWSKI;
    training.setDistanceType(distanceType);

    //value for q
    double qValue = 0.25;
    training.setQforMinkowski(qValue);

    //k-value
    training.setNumOfWinesForAveragePredict(3);

    //First prediction step -> wines will be classified to red/white
    all = evaluate(training, testing, all);

    //Split training data to red and white
    std::pair<Cellar, Cellar> redAndWhiteTraining = training.splitToRedAndWhite();

    // Split to red and white wines based on predictions
    std::pair<std::vector<Wine*>, std::vector<Wine*> > redAndWhiteTesting = Cellar::splitWineListToRedAndWhite(testing);


    // Second prediction step -> wine quality classification
    //Red wines against red wines
    red = evaluate(redAndWhiteTraining.first, redAndWhiteTesting.first, red);
    //White wines against white wines
    white = evaluate(redAndWhiteTraining.second, redAndWhiteTesting.second, white);

    //Check results
    int total = red.correct_ones() + white.correct_ones();
    std::cout << "One step prediction: " << all.correct_ones() << "/" << testing.size() << std::endl;
    std::cout << "Two step prediction: " << total << "/" << testing.size() << std::endl;

    /**
    One step prediction: 628/1000
    Two step prediction: 630/1000

    ==> TWO STEP ACTUALLY LITTLE BETTER
    **/
}

